unit GLGUILayer;

interface

{$I GLScene.inc}

uses
  Classes,
  VectorTypes,
  VectorGeometry,
  GLScene,
  GLState,
  GLRenderContextInfo,
  GLGUIInputManager,
  GLGUITokens,
  GLGUIBase,
  GLGUIEvent,
  GLGUI2DSprite,
  GLGUIEventManager,
  GLGUIShaders,
  GLGUIExceptions;

type
  TGLGUI2DStage = class;

  TGLGUILayer = class(TGLGUIObject)
  protected
    FLayerMode: TGLGUILayerMode;
    FWidth: Single;
    FHeight: Single;
    FScissorTester: TObject;
    FLayerUpdateEvent: TGLGUIEvent;
    FStage: TGLGUI2DStage;
    FCurrentID: Cardinal;
    FGUIObjects: TGLGUIObjects;
    function GetWidth: Single; virtual;
    function GetHeight: Single; virtual;
    function GetScissorTester: TObject; virtual;
    procedure SetScissorTester(ATester: TObject); virtual;
    function GetBox: TVector4i;
    function GetGUIObject(AIndex: Integer): TGLGUIObject;
  public
    constructor Create(AOwner: TComponent); override;
    procedure AddChild(AChild: TGLBaseSceneObject); override;
    procedure RegisterGUIChild(AChild: TGLGUIObject); virtual;
    procedure Enable; override;
    procedure Disable(AKeepState: Boolean = False); override;
    procedure DispatchMouseDownEvent(const AExternal: Boolean = True); override;
    procedure DispatchMouseUpEvent(const AExternal: Boolean = True); override;
    procedure DispatchMouseWheelEvent(const AExternal: Boolean = True); override;
    procedure DispatchKeyDownEvent(const AExternal: Boolean = True); override;
    procedure DoRender(var ARci: TRenderContextInfo;
      ARenderSelf, ARenderChildren: Boolean); override;
    procedure AddOnUpdateEventListener(AListener: TGLGUIEventListener);
    destructor Destroy; override;
    property Width: Single read GetWidth;
    property Height: Single read GetHeight;
    property ScissorTester: TObject read GetScissorTester
      write SetScissorTester;
    property Box: TVector4i read GetBox;
    property OnLayerUpdate: TGLGUIEvent read FLayerUpdateEvent;
    property Stage: TGLGUI2dStage read FStage;
    property GUIObjects[AIndex: Integer]: TGLGUIobject read GetGUIObject;
  end;

  TGLGUI2DStage = class(TGLGUI2DSpriteCustom)
  protected
    procedure OnLayerUpdate(AEvent: TGLGUIEvent);
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

constructor TGLGUILayer.Create(AOwner: TComponent);
begin
  inherited;
  FGUIObjects := TGLGUIObjects.Create;
  FLayerMode := glm2D;
  GLInputManager.RegisterUser(Self);
  FLayerUpdateEvent := GUIEventManager.GetOrCreateEvent('GLGUILayer.Update');
  FCurrentID := 0;
end;

function TGLGUILayer.GetWidth: Single;
begin
  Result := FWidth;
end;

function TGLGUILayer.GetHeight: Single;
begin
  Result := FHeight;
end;

function TGLGUILayer.GetScissorTester: TObject;
begin
  Result := FScissorTester;
end;

procedure TGLGUILayer.SetScissorTester(ATester: TObject);
begin
  FScissorTester := ATester;
end;

function TGLGUILayer.GetBox: TVector4i;
begin
  Result := Vector4iMake(0, 0, Round(Width), Round(Height));
end;

function TGLGUILayer.GetGUIObject(AIndex: Integer): TGLGUIObject;
begin
  Result := FGUIObjects[AIndex];
end;

procedure TGLGUILayer.AddChild(AChild: TGLBaseSceneObject);
begin
  if AChild is TGLGUIObject then
  begin
    case FLayerMode of
      glm2D:
        if AChild is TGLGUI2D then
          inherited AddChild(AChild);
      glm3D:;
    end;
  end;
end;

procedure TGLGUILayer.RegisterGUIChild(AChild: TGLGUIObject);
begin
  FGUIObjects.Add(TGLGUIObject(AChild));
  TGLGUIObject(AChild).Id := FGUIObjects.Count - 1;
end;

procedure TGLGUILayer.DoRender(var ARci: TRenderContextInfo;
  ARenderSelf, ARenderChildren: Boolean);
var
  i: Integer;
  vWidth, vHeight: Single;
begin
  if FLayerMode = glm2D then
  begin
    with ARci.GLStates do
      if not (stScissorTest in States) then
        Enable(stScissorTest);
  end;

  if ARenderChildren then
    for i := 0 to Count - 1 do
    begin
      Children[i].Render(ARci);
      if stScissorTest in ARci.GLStates.States then
      begin
        if FScissorTester <> Self then
        begin
          ARci.GLStates.ScissorBox := Box;
          FScissorTester := Self;
        end;
      end;
    end;

  if stScissorTest in ARci.GLStates.States then
    ARci.GLStates.Disable(stScissorTest);

  vWidth := ARci.viewPortSize.cx;
  vHeight := ARci.viewPortSize.cy;

  if (vWidth <> FWidth) then
  begin
    FWidth := vWidth;
    FLayerUpdateEvent.DispatchEvent(Self);
  end;

  if (vHeight <> FHeight) then
  begin
    FHeight := vHeight;
    FLayerUpdateEvent.DispatchEvent(Self);
  end;

end;

procedure TGLGUILayer.Enable;
var
  i: Integer;
begin
  inherited;
  for i := 0 to Count - 1 do
    TGLGUIObject(Children[i]).Enable;
end;

procedure TGLGUILayer.Disable(AKeepState: Boolean = False);
var
  i: Integer;
begin
  inherited;
  for i := 0 to Count - 1 do
    TGLGUIObject(Children[i]).Disable;
end;

procedure TGLGUILayer.DispatchMouseDownEvent(const AExternal: Boolean = True);
var
  i: Integer;
begin
  for i := Count - 1 downto 0 do
    if Children[i] is TGLGUIObject then
      (Children[i] as TGLGUIObject).DispatchMouseDownEvent(False);

  if FEnabled then
    MouseDownEvent.DispatchEvent(Self);
end;

procedure TGLGUILayer.DispatchMouseUpEvent(const AExternal: Boolean = True);
var
  i: Integer;
begin
  for i := Count - 1 downto 0 do
    if Children[i] is TGLGUIObject then
      (Children[i] as TGLGUIObject).DispatchMouseUpEvent(False);

  if FEnabled then
    MouseUpEvent.DispatchEvent(Self);
end;

procedure TGLGUILayer.DispatchMouseWheelEvent(const AExternal: Boolean = True);
var
  i: Integer;
begin
  for i := Count - 1 downto 0 do
    if Children[i] is TGLGUIObject then
      (Children[i] as TGLGUIObject).DispatchMouseWheelEvent(False);

  with MouseWheelEvent do
  begin
    if FEnabled then
      if PointInObject(VectorMake(X, Y, 0, 0)) then
        DispatchEvent(Self);
  end;
end;

procedure TGLGUILayer.DispatchKeyDownEvent(const AExternal: Boolean = True);
var
  i: Integer;
begin
  for i := Count - 1 downto 0 do
    if Children[i] is TGLGUIObject then
      (Children[i] as TGLGUIObject).DispatchKeyDownEvent(False);

  if FEnabled then
    KeyDownEvent.DispatchEvent(Self);
end;

procedure TGLGUILayer.AddOnUpdateEventListener(AListener: TGLGUIEventListener);
begin
  AddEventListener(OnLayerUpdate, AListener);
end;

destructor TGLGUILayer.Destroy;
begin
  FGUIObjects.Free;
  inherited;
end;

constructor TGLGUI2DStage.Create(AOwner: TComponent);
begin
  if AOwner is TGLGUILayer then
  begin
    inherited;
    with TGLGUILayer(FLayer) do
      AddEventListener(OnLayerUpdate, Self.OnLayerUpdate);
    EventBehaviour := ebPassEvent;
  end
  else
    raise EGLGUIException.Create(EStageExeption);

  TGLGUILayer(FLayer).FStage := Self;
end;

procedure TGLGUI2DStage.OnLayerUpdate(AEvent: TGLGUIEvent);
begin
  Width := TGLGUILayer(FLayer).Width;
  Height := TGLGUILayer(FLayer).Height;
  Position.Y := Height/2;
  Position.X := Width/2;
end;

end.
